/**
 * @brief   RUDP协议实现模块。
 * @version 1.0
 * @date    2018-3-2
 * @author  宋炜
*/

#ifndef __RDUP_HPP__
#define __RDUP_HPP__
#include <string>
#include <atomic>
#include <uv.h>
#include "timer.hpp"

class rdup
{
	friend void on_rudp_recv(uv_udp_t* handle, ssize_t nread, const uv_buf_t* buf, const struct sockaddr* addr, unsigned flags);
	friend void on_rudp_send(uv_udp_send_t* req, int status);
	friend void on_rudp_getaddrinfo(uv_getaddrinfo_t* req, int status, struct addrinfo* res);
public:
	/**
	 * 
	 */
	enum err_code{
		ERR_BASE = ERR_RDUP,
		ERR_INIT_UDP,                         // 初始化UDP接口失败
		ERR_INIT_UV,                          // 初始化libuv失败
		ERR_INIT_UV_LOOP,					  // 初始化时间循环失败
		ERR_RESOLVE_ADD,                      // 解析地址失败
		ERR_OVERTIME,						  // 操作超时
		ERR_NULL_ADDRINFO,                    // addrinfo 指针空
		ERR_ALLOC_MEM,						  // 分配内存失败。
		ERR_LIBUV,							  // libuv 操作错误
		OK = 0
	};
	
	/* 协议数据包 */
	typedef struct stprotocol{
		uint32_t            msg_id;            // 消息序号
		struct qos_ip{ 
			int           	qos:3;             // QOS
			int           	:5;                // 空
			int           	ip_type:8;         // IP 类型
		};
		uint16_t            opt;               // 数据包类型
		uint32_t            len;               // 数据长度
		unsigned char     * data;              // 数据

		stprotocol();		
	}rudp_pkge_t;
	
	
	/* 协议缓冲区 ， 发送的数据但是没有接收到反馈的数据记录在缓冲中 */
	
	typedef struct stprotocol_data{
		char * data;
		size_t len;

		stprotocol_data( const rudp_pkge_t& packet );
		~stprotocol_data();
	}rdup_buf_t;
	/**
	 * 队列内容
	 */
	typedef struct queueItem{
		rudp_pkge_t		m_data;							// 数据包
		int             m_overtime_count;               // 重传次数

	}rudp_item_t;

	typedef std::list< queueItem >  sendQueue_t;

	const static uint8_t QOS0         = 0x0;            // 不重发，不接收响应
	const static uint8_t QOS1         = 0x01;           // 不重发，接收响应
	const static uint8_t QOS2         = 0x02;           // 连续重发 ， 接收响应;在没有接收到响应前按照50ms间隔连续重发消息
	const static uint8_t QOS3         = 0x03;           // 指数退避
	const static uint8_t QOS4         = 0x04;           // 斐波那契退避。退避操作以100ms为基本的退避单位
	const static uint8_t QOS5         = 0X05;           // 连续重发5次，不接收响应
	/* 操作类型定义 */
	const static uint16_t SEND        = 0X1000;         // 发送操作数据包
	const static uint16_t SEND_E      = 0X0010;         // 发送反馈包
	const static uint16_t KEEPALIVE   = 0X2000;
	const static uint16_t KEEPALIVE_E = 0X0020;
	const static uint16_t PING        = 0X4000;
	const static uint16_t PING_E      = 0X0040;
	const static uint16_t CONNECT     = 0X8000;
	const static uint16_t CONNECT_E   = 0X0080;
	const static uint16_t CLOSE       = 0XA000;
	const static uint16_t CLOSE_E     = 0X00A0
	/* 指数退避时钟索引表 */
	const static int EXP_ARY[] = {0 , 2 , 7 20 , 54 , 148 , 403 , 1096 , 8103 };
	/* 斐波那契数列退避索引表 */
	const static int FB_ARY[] = { 1 , 1 , 2 ,3 ,5 , 8 ,13 ,21 ,34 , 55 };
	/**
	 * 操作状态定义。
	*/
	const static int  S_READY		  = 1;
	const static int  S_CONNECTING    = 2;
	const static int  S_CONNECTED     = 3;
	const static int  S_CLOSING       = 4;
	const static int  S_CLOSED        = 5;
	const static int  S_SENDING       = 6;
	const static int  S_RECVING       = 7;
private:
	std::string             url;
	int                     port;
	struct sockaddr         m_peer;                     // 远程端点
	
	err_code                m_error;
	int                     send_count;                 // 当前数据重发计数

	uv_loop_t             * p_loop;
	uv_udp_t                m_udp;

	std::atomic< int >      m_overtime;					// 操作超时时间
	CTimer                  m_opt_timer;                // 操作的计时器
	std::atomic< int >      m_status;                   // 操作状态 ， 准备就绪，连接中，连接，断开中，断开
														// 发送数据中，接收中，
	std::atomic< uint32_t > m_msg_id;                   // 消息ID，从 0 每次发送消息后增加 1， 周而复始。
	sendQueue_t             m_queue;                    // 

	std::function< void ( err_code ) >     connect_cb;
	std::function< void ( err_code ) >     close_cb;
	std::function< void ( err_code ) >     send_cb;
	std::function< void ( const char * , size_t , err_code)>  recv_cb;
	std::function< void ( err_code ) >     ping_cb;
private:
	/**
	 * @brief 初始化Libuv
	 */
	void init_uv();
	
	/**
	 * @brief 接收到原始的UDP数据相应函数
	 * @param data 接收到的数据指针
	 * @param len 接收到数据的长度
	 */
	void on_raw_recv( const char * data , size_t len );
	/**
	 * @brief 原始的UDP数据包发送完成后会调用这个函数。并返回数据包的指针，这个指针可以用来分析数据结构
	 * @param package
	 */
	void on_raw_send( rudp_pkge_t * package );
	
	/**
	 * @brief 打包用户数据数据
	 */
	void packet( const char * data , size_t len , rudp_pkge_t * pkge );
	/**
	 * @brief 构造反馈包
	 */
	void packet_e( const char * pkgein , rudp_pkget_t * pkgeout );
	
	void packet_ping( rudp_pkge_t * pkge );
	void packet_ping_e( const char * pkgein , rudp_pkget_t * pkgeout );
	
	void packet_connect( rudp_pkge_t *pkge );
	void packet_connect_e( const char * pkgein , rudp_pkget_t * pkgeout );
	
	void packet_close( rudp_pkge_t * pkge );
	void packet_close_e( const char * pkgein , rudp_pkget_t * pkgeout );
	
	void packet_keepalive( rudp_pkge_t * pkge );
	void packet_keepalive_e( const char * pkgein , rudp_pkget_t * pkgeout );
	/**
	 * 解包数据
	 */
	void unpacket( const char * data , size_t len , rudp_pkge_t * pkge );

	/**
	 * @brief RUDP 的连接操作，构造连接数据包发送数据包
	 */
	void real_connect();
	void pre_connect( );
	/**
	 * @brief UDP数据发送操作
	 */
	err_code udp_send( const char * data , size_t len );
	/**
	 * @brief 获取退避数组；
	 * @param packet
	 * @return 如果不需要退避操作则返回空指针；否则返回数组头指针
	 */
	const int * get_retrait_array( const rudp_pkge_t& packet );
public:
	/**
	 * @brief 构造对象
	 * @exception err_code 
	 */
	rudp();
	rudp( const std::string& url , int port );
	virtual ~rdup();
	/**
	 * @brief 连接服务器。
	 * @param url 服务器地址 ， 地址可以是URL也可以直接IP地址
	 * @param port 服务器端口
	 * @return 返回操作结果。成功返回OK，否则是错误代码
	 */
	err_code connect( const std::string& url , int port );
	/**
	 * @brief 关闭连接
	 */
	err_code close();
	/**
	 * @brief 设置关闭回调函数，当完断开连接操作后，模块会调用这里设置回调函数
	 * @param cb ，回调函数对象,函数原型为如下：
	 *      void onClose( err_code e );
	 *   当正确关闭传入的参数e是OK，否则就是执行的错误代码
	 */
	err_code on_close( std::function<void(err_code)> cb );
	/**
	 * @brief 设置关闭回调函数，当完断开连接操作后，模块会调用这里设置回调函数
	 * @param cb 回调函数对象,函数原型为如下：
	 * 	 void onConnected( err_code e );
	 */
	err_code on_connected( std::function< void ( err_code )> cb );
	/**
	 * @brief 发送数据
	 */
	err_code send( const char * data , size_t len );
	/**
	 * @brief 设置发送完成回调函数
	 * @param cb 回调函数对象
	 * 		void  cb( err_code e );
	 */
	void onsend( std::function<void ( err_code ) > cb );
	/**
	 * @brief 设置接收回调函数
	 */
	void on_recv( std::function<void ( const char * , size_t , err_code )> cb );
	/**
	 * @brief 包活机制。
	 * @param sw
	 * @param intval , 包活包间隔时间单位ms
	 */
	void keepalive( bool sw , long intval );
	/**
	 * @brief 发送可达探测包
	 */
	err_code ping( int intval , int count = -1 );
	err_code on_ping( std::function< void ( err_code )> cb );
	/**
	 * @brief 超时等候，或者判断是否发生错误,如果超时则抛出超时异常，如果发生错误册抛出错误异常。
	 *    执行超时等候的时候，会检查err 的错误是否发生和cancel条件是否满足。如果满足了停止条件则
	 *    会立即停止执行。
	 * @param err 
	 * @param cancel , 取消执行条件.取消执行条件可以是一个判断的函数也可以是一个布尔型的指示器。
	 * @exception err_code
	 * @note 以下者三个函数的执行都会将主线程阻塞起来。使用的时候应特别注意。
	 */
	void wait_and_err( std::atomic< err_code > & err , std::function< bool () > cancel );
	void wait_and_err( std::atomic< err_code > & err , std::atomic<bool>& cancel );
	/**
	 * @brief 这个函数不会抛出异常二是执行回调操作。
	 * @param fun 回调函数；void fun( err_code e );
	 */
	void wait_and_err( std::atomic< err_code > &err , std::function< bool () > cancel , std::function< void ( err_code ) > fun );
	void wait_and_err( std::atomic< err_code > &err , std::atomic< bool> cancel , std::function< void ( err_code ) > fun );
	/**
	 * @brief 执行连接操作。
	 */
	void do_connect( struct addrinfo * info );
};

#endif
